﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DigitalRune.Windows.Docking;
using md.imi.membrane.common.Interfaces;
using md.imi.membrane.common.Messaging;
using md.imi.membranes.gui.Dialogs;
using md.imi.membranes.objects;
using md.imi.membranes.objects.membranes;
using md.imi.membranes.objects.psystems;
using md.imi.membranes.objects.strings;


/**
 *      Configurations DIFF view is used to display the diferencies between 2
 *  immediate neighbors configurations.
 *  
 */

namespace md.imi.membranes.gui.Info
{


    public partial class ConfigDiffsView : DockableForm, IMessageViewer
    {
        public object syncObject = new object();

        private static readonly Color clrAddedObjectForeground      = Color.ForestGreen;
        private static readonly Color clrAddedObjectBackground      = Color.Gainsboro;
        private static readonly Color clrRemovedObjectForeground    = Color.Firebrick;
        private static readonly Color clrRemovedObjectBackground    = Color.Gainsboro;
        // private static readonly Color clrModifiedObjectForeground   = Color.CornflowerBlue;
        // private static readonly Color clrModifiedObjectBackground   = Color.Gainsboro;


        private static readonly Color clrAddedMembraneForeground = Color.ForestGreen;
        private static readonly Color clrRemovedMembraneForeground = Color.Firebrick;
        private static readonly Color clrAddedMebraneBackground = Color.Gainsboro;
        private static readonly Color clrRemovedMembraneBackground = Color.Gainsboro;


        public ConfigDiffsView()
        {
            InitializeComponent();

        }

        #region Implementation of IMessageViewer

        /// <summary>
        ///     Add a mesage to the view, taking into account the cross 
        /// thread calling
        /// </summary>
        /// <param name="message"></param>
        public void AddMessage(MessageData message)
        {
            //---- 
            // 
            LogsView.Info("Add new message to DIFF window " + message.ToString());
            // throw new NotImplementedException();
        }

        /// <summary>
        ///     Clear the view, by removin
        /// </summary>
        public void ClearView()
        {
            throw new NotImplementedException();
        }

        public DestinationType GetDestinationType()
        {
            return DestinationType.GeneratedLanguageTarget;
            // throw new NotImplementedException();
        }

        #endregion

        #region Calculate differentiations
        public void ShowMembranesDiff(MembranesTree m1, MembranesTree m2)
        {
            var membraneGroup = lvChanges.Groups.Add(
                String.Format("Membrane-{0}", m1.Id),
                String.Format("Membrane-{0}", m1.Label));

            membraneGroup.HeaderAlignment = HorizontalAlignment.Right;
            
            if (m1.Id.Equals(m2.Id))
            {
                ShowMembanesContentDiff(membraneGroup, m1, m2);
            }

            var remainedMembranes = new List<List<MembranesTree>>();
            var addedMembranes = new List<MembranesTree>();
            var removedMembranes = new List<MembranesTree>();


            // find added and unchanged membranes
            foreach (var sm2 in m2.Membranes)
            {
                if (m1.Membranes.Any(sm1 => sm2.Id.Equals(sm1.Id)))
                {
                    var lsm = new List<MembranesTree>();
                    lsm.Add(sm2);
                    remainedMembranes.Add(lsm);
                }
                else
                    addedMembranes.Add(sm2);
            }

            // find removed membranes, and add the second pair of remained one
            foreach (var sm1 in m1.Membranes)
            {
                if (m1.Membranes.Any(sm2 => sm1.Id.Equals(sm2.Id)))
                {
                    foreach (var lsm in remainedMembranes)
                    {
                        if (lsm[0].Id.Equals(sm1.Id))
                        {
                            lsm.Add(sm1);
                            break;
                        }
                    }
                }
                else
                    removedMembranes.Add(sm1);
            }


            // show in green color, as added membranes
            ShowAddedMembranesDiff(membraneGroup, addedMembranes);
            ShowRemovedMembranesDiff(membraneGroup, removedMembranes);

            // call recursivelly the investigation for next set of the same membranes
            foreach (var remainedMembranePair in remainedMembranes)
            {
                // m2, m1
                if (remainedMembranePair.Count == 2)
                    ShowMembranesDiff(remainedMembranePair[0], remainedMembranePair[1]);
            }

        }
        #endregion

        #region Interface functions
        /**
         *      Recursivelly go in all membranes, and ceheck if any changes occured
         *
         *   ps1- is considered system  from previos step, 
         *   ps2- is considered system of next step
         */
        public void ShowNodesDiff(PSystemsTree ps1, PSystemsTree ps2)
        {
            if (ps1 == null || ps2 == null)
            {
                FrmMessageBox.ShowError("Fatal error. Passed psystems for comparations are not set!");
                return;
            }

            lvChanges.Groups.Clear();
            lvChanges.Items.Clear();

            lvChanges.Columns[0].Text = String.Format("Evolution from {0} -> {1}",
                ps1.Label, ps2.Label);

            ShowMembranesDiff(ps1.Membranes, ps2.Membranes);
        }
        #endregion

        #region Display DIFF
        // diff membranes topolog (recursivelly)
        private void ShowAddedMembranesDiff(ListViewGroup membraneGroup, List<MembranesTree> addedMembranes)
        {
            if (addedMembranes == null || addedMembranes.Count == 0)
            {
                return;
            }

            var sb = new StringBuilder("------- New added membranes ----");
        
            sb.AppendLine();
            foreach (var addedMembrane in addedMembranes)
            {
                sb.AppendFormat("Membrane: {0}", addedMembrane.Label).AppendLine();
                var lvItem = new ListViewItem("+ " + addedMembrane.Label, membraneGroup);

                lvItem.BackColor = clrAddedMebraneBackground;
                lvItem.ForeColor = clrAddedMembraneForeground;
                lvChanges.Items.Add(lvItem);
            }

            tbInfo.Text += sb.ToString();

        }

        // diff membranes topolog (recursivelly)
        private void ShowRemovedMembranesDiff(ListViewGroup membraneGroup, List<MembranesTree> removedMembranes)
        {
            if (removedMembranes == null || removedMembranes.Count == 0)
            {
                return;
            }

            var sb = new StringBuilder("------- Removed membranes ----");

            sb.AppendLine();
            foreach (var removedMembrane in removedMembranes)
            {
                sb.AppendFormat("Membrane: {0}", removedMembrane.Label).AppendLine();

                var lvItem = new ListViewItem("- " + removedMembrane.Label, membraneGroup);

                lvItem.BackColor = clrRemovedMembraneBackground;
                lvItem.ForeColor = clrRemovedMembraneForeground;
                lvChanges.Items.Add(lvItem);
            }

            tbInfo.Text += sb.ToString();

        }

        // 2. diff membranes content (do a difference of the same level). 
        // Detection of modified objects is possible if an ID is introduced.
        public void ShowMembanesContentDiff(ListViewGroup membraneGroup, MembranesTree m1, MembranesTree m2)
        {
            // perform a differentiation between content of two membranes
            // Check added String Objects, Removed and Modified ... ?

            var addedObjects = new List<StringObject>();
            var removedObjects = new List<StringObject>();
            // I don't know yet how to determine that a string has been modified (By GuID ?)
            var modifiedObjects = new List<StringObject>();

            // m1 is the state 1
            // m2 is the state 2
            // added SO are considered SO that are missing in m1 (comparing with m2)
            // removed SO are considered SO that are miss

            // 1.
            // detect added objects (present in m2
            foreach (var so2 in m2.Multiset)
            {
                bool isContained = false;
                foreach (var so1 in m1.Multiset)
                {
                    if (so2.Equals(so1))
                    {
                        isContained = true;
                        break;
                    }
                }

                if (!isContained) addedObjects.Add(so2);
            }


            // 1. detect removed objects (present in m1
            foreach (var so1 in m1.Multiset)
            {
                bool isContained = false;
                foreach (var so2 in m2.Multiset)
                {
                    if (so2.Equals(so1))
                    {
                        isContained = true;
                        break;
                    }
                }

                if (!isContained) removedObjects.Add(so1);
            }

            var sb = new StringBuilder("");

            if (addedObjects.Count > 0)
            {
                sb.AppendFormat("------- New added String objects to membrane {0} ----", m2.Label);
                sb.AppendLine();
                foreach (var addedObject in addedObjects)
                {
                    sb.AppendFormat("SObject: {0}", addedObject.ToString()).AppendLine();
                    var lvItem = new ListViewItem("+ " + addedObject.ToString(), membraneGroup);
                    lvItem.BackColor = clrAddedObjectBackground;
                    lvItem.ForeColor = clrAddedObjectForeground;

                    lvChanges.Items.Add(lvItem);
                }

                tbInfo.Text += sb.ToString();
            }

            sb = new StringBuilder("");

            if (removedObjects.Count > 0)
            {
                sb.AppendFormat("------- Removed String objects from membrane {0}----", m1.Label);
                sb.AppendLine();
                foreach (var removedObject in removedObjects)
                {
                    sb.AppendFormat("SObject: {0}", removedObject.ToString()).AppendLine();
                    var lvItem = new ListViewItem("- " + removedObject.ToString(), membraneGroup);

                    lvItem.BackColor = clrRemovedObjectBackground;
                    lvItem.ForeColor = clrRemovedObjectForeground;
                    lvChanges.Items.Add(lvItem);
                }

                tbInfo.Text += sb.ToString();
            }
        }
        #endregion
    }

}